<template >
  <div class="uploader_dialog" :class="{ 'active': collapse }">
    <uploader
      ref="uploader"
      :options="options"
      :autoStart="false"
      @file-added="onFileAdded"
      @file-success="onFileSuccess"
      @file-progress="onFileProgress"
      @file-error="onFileError"
      class="uploader-app"
    >
      <uploader-unsupport></uploader-unsupport>

      <uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn">选择文件</uploader-btn>

      <uploader-list v-show="panelShow">
        <div class="file-panel" slot-scope="props" :class="{'collapse': collapse}">
          <div class="dialog-header">
            <div class="dialog-header-title">文件列表</div>
            <div class="action">
              <i
                :class="!collapse ? 'el-icon-minus' : 'el-icon-plus'"
                @click="collapse = !collapse"
              ></i>
              <i class="el-icon-circle-close" style="margin-left: 10px;" @click="visible = false"></i>
            </div>
          </div>

          <ul class="file-list">
            <li v-for="file in props.fileList" :key="file.id">
              <uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
            </li>
            <div class="no-file" v-if="!props.fileList.length">
              <i class="iconfont icon-empty-file"></i> 暂无待上传文件
            </div>
          </ul>
        </div>
      </uploader-list>
    </uploader>
  </div>
</template>

<script>
import uploader from "vue-simple-uploader";
/**
 *   全局上传插件
 *   调用方法：Bus.$emit('openUploader', {}) 打开文件选择框，参数为需要传递的额外参数
 *   监听函数：Bus.$on('fileAdded', fn); 文件选择后的回调
 *            Bus.$on('fileSuccess', fn); 文件上传成功的回调
 */
import { bus as Bus } from "@/utils/index";
import SparkMD5 from "spark-md5";

const ACCEPT_CONFIG = {
  image: [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
  video: [".mp4", ".rmvb", ".mkv", ".wmv", ".flv"],
  document: [
    ".doc",
    ".docx",
    ".xls",
    ".xlsx",
    ".ppt",
    ".pptx",
    ".pdf",
    ".txt",
    ".tif",
    ".tiff"
  ],
  getAll() {
    return [...this.image, ...this.video, ...this.document];
  }
};

export default {
  components: {
    uploader
  },
  data() {
    return {
      params: null,
      options: {
        target: "/uploads/upload/chunkUpload/upload_do",
        // chunkSize: "600",
        fileParameterName: "file",
        maxChunkRetries: 3,
        testChunks: false, //是否开启服务器分片校验
        // 服务器分片校验函数，秒传及断点续传基础
        checkChunkUploadedByResponse: function(chunk, message) {
          // let objMessage = JSON.parse(message);
          // if (objMessage.skipUpload) {
          //   return true;
          // }

          // return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0;
        },
        query() {}
      },
      attrs: {
        accept: ACCEPT_CONFIG.getAll()
      },
      panelShow: true, //选择文件后，展示上传panel
      collapse: false
    };
  },
  mounted() {
    Bus.$on("openUploader", query => {
      this.params = query || {};
      if (this.$refs.uploadBtn) {
        document.querySelector("#global-uploader-btn").click();
      }
    });
  },
  computed: {
    //Uploader实例
    uploader() {
      return this.$refs.uploader.uploader;
    }
  },
  methods: {
    onFileAdded(file) {
      this.panelShow = true;
      this.computeMD5(file);

      Bus.$emit("fileAdded");
    },
    onFileProgress(rootFile, file, chunk) {
      console.log(
        `上传中 ${file.name}，chunk：${chunk.startByte /
          1024 /
          1024} ~ ${chunk.endByte / 1024 / 1024}`
      );
    },
    onFileSuccess(rootFile, file, response, chunk) {
      let res = JSON.parse(response);

      // 服务器自定义的错误（即虽返回200，但是是错误的情况），这种错误是Uploader无法拦截的
      if (!res.result) {
        this.$message({ message: res.message, type: "error" });
        // 文件状态设为“失败”
        this.statusSet(file.id, "failed");
        return;
      }

      // 如果服务端返回需要合并
      if (res.needMerge) {
        // 文件状态设为“合并中”
        this.statusSet(file.id, "merging");

        api
          .mergeSimpleUpload({
            tempName: res.tempName,
            fileName: file.name,
            ...this.params
          })
          .then(res => {
            // 文件合并成功
            Bus.$emit("fileSuccess");

            this.statusRemove(file.id);
          })
          .catch(e => {});

        // 不需要合并
      } else {
        Bus.$emit("fileSuccess");
        console.log("上传成功");
      }
    },
    onFileError(rootFile, file, response, chunk) {
      this.$message({
        message: response,
        type: "error"
      });
    },

    /**
     * 计算md5，实现断点续传及秒传
     * @param file
     */
    computeMD5(file) {
      let fileReader = new FileReader();
      let time = new Date().getTime();
      let blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice;
      let currentChunk = 0;
      const chunkSize = 10 * 1024 * 1000;
      let chunks = Math.ceil(file.size / chunkSize);
      let spark = new SparkMD5.ArrayBuffer();

      // 文件状态设为"计算MD5"
      this.statusSet(file.id, "md5");
      file.pause();

      loadNext();

      fileReader.onload = e => {
        spark.append(e.target.result);

        if (currentChunk < chunks) {
          currentChunk++;
          loadNext();

          // 实时展示MD5的计算进度
          // this.$nextTick(() => {
          //   document.querySelector(`.myStatus_${file.id}`).innerHTML = "校验MD5 " + ((currentChunk / chunks) * 100).toFixed(0) + "%"
          // });
        } else {
          let md5 = spark.end();
          this.computeMD5Success(md5, file);
          console.log(
            `MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${
              file.size
            } 用时：${new Date().getTime() - time} ms`
          );
        }
      };

      fileReader.onerror = function() {
        this.error(`文件${file.name}读取出错，请检查该文件`);
        file.cancel();
      };

      function loadNext() {
        let start = currentChunk * chunkSize;
        let end =
          start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
      }
    },

    computeMD5Success(md5, file) {
      // 将自定义参数直接加载uploader实例的opts上
      Object.assign(this.uploader.opts, {
        query: {
          ...this.params
        }
      });

      file.uniqueIdentifier = md5;
      file.resume();
      this.statusRemove(file.id);
    },

    fileListShow() {
      let $list = document.querySelector("#global-uploader .file-list");

      if ($list.is(":visible")) {
        $list.slideUp();
        this.collapse = true;
      } else {
        $list.slideDown();
        this.collapse = false;
      }
    },
    close() {
      this.uploader.cancel();

      this.panelShow = false;
    },

    /**
     * 新增的自定义的状态: 'md5'、'transcoding'、'failed'
     * @param id
     * @param status
     */
    statusSet(id, status) {
      let statusMap = {
        md5: {
          text: "校验MD5",
          bgc: "#fff"
        },
        merging: {
          text: "合并中",
          bgc: "#e2eeff"
        },
        transcoding: {
          text: "转码中",
          bgc: "#e2eeff"
        },
        failed: {
          text: "上传失败",
          bgc: "#e2eeff"
        }
      };

      // this.$nextTick(() => {
      //   // h("p", `myStatus_${id}`)
      //   $(`<p class="myStatus_${id}"></p>`)
      //     .appendTo(`.file_${id} .uploader-file-status`)
      //     .css({
      //       position: "absolute",
      //       top: "0",
      //       left: "0",
      //       right: "0",
      //       bottom: "0",
      //       zIndex: "1",
      //       backgroundColor: statusMap[status].bgc
      //     })
      //     .text(statusMap[status].text);
      // });
    },
    statusRemove(id) {
      // this.$nextTick(() => {
      //   document.querySelector(`.myStatus_${id}`).remove();
      // });
    },

    error(msg) {
      this.$notify({
        title: "错误",
        message: h('span', { style: 'word-break:break-all'}, msg),
        type: "error",
        duration: 2000
      });
    }
  },
  watch: {},
  destroyed() {
    Bus.$off("openUploader");
  },
  components: {}
};
</script>

<style scoped lang="scss">
  @import "@/styles/mixin.scss";
.uploader_dialog {
  position: fixed;
  right: 30px;
  bottom: 0;
  width: 635px;
  height: 430px;
  border-top-left-radius: 7px;
  border-top-right-radius: 7px;
  border: 1px solid $--border-color-base;
  box-shadow: 0 0 10px #ccc;
  margin-bottom: -2px;
  background: #fff;
  transition: all 0.5s ease;
  &.active {
    bottom: -385px;
  }
  .dialog-header {
    padding: 0 20px;
    border-bottom: 1px solid #e3e3e3;
    height: 44px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .dialog-header-title {
      color: #666;
      font-weight: 200;
      font-style: normal;
    }
    .action {
      i {
        cursor: pointer;
        font-weight: bold;
      }
    }
  }
  .uploader-list {
    li {
      padding: 13px;
      display: flex;
      align-items: center;
      width: 100%;
      height: 50px;
      border-bottom: 1px solid #f2f6fd;
      & > div {
        font-size: 12px;
      }
      .file-name {
        width: 43%;
      }
      .file-size,
      .file-category {
        text-indent: 13px;
        width: 13%;
      }
      .file-category {
        color: #ff803a;
      }
      .file-process {
        width: 21%;
      }
      .file-remove {
        width: 10%;
        cursor: pointer;
        text-align: right;
      }
    }
  }

    .file-list {
      position: relative;
      height: calc(430px - 45px);
      overflow-x: hidden;
      overflow-y: auto;
      background-color: #fff;

      > li {
        background-color: #fff;
        > div {
          width: 100%;
        }
      }
    }

    &.collapse {
      .file-title {
        background-color: #e7ecf2;
      }
    }

  .no-file {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 16px;
  }

  /deep/.uploader-file-icon {
    &:before {
      content: "" !important;
    }

    &[icon="image"] {
      background: url(./images/image-icon.png);
    }
    &[icon="video"] {
      background: url(./images/video-icon.png);
    }
    &[icon="document"] {
      background: url(./images/text-icon.png);
    }
  }

  /deep/.uploader-file-actions > span {
    margin-right: 6px;
  }
}
/* 隐藏上传按钮 */
#global-uploader-btn {
  position: absolute;
  clip: rect(0, 0, 0, 0);
}
</style>
